#!/usr/bin/python3
# -*- coding:utf-8 -*-
""""
@Author: 李吉凯
@Email: lijk34925@hundsun.com
@Time: 2022/5/12 18:10
@File: src.py
"""
import re
import traceback

from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys

from qturboframe.lib.webdriver import SeleniumError
from selenium.common.exceptions import TimeoutException

from BladeFerm.Lib.Common import logger
from qturboframe.lib.utils.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


def get_params(idname: dict):
    '''
    根据入参分割 元素查找属性和辅助属性
    :param element_para: - idname: 下拉框的定位条件，字典类型：{'元素查找方式':'元素查找语句', '辅助属性':'辅助属性值'}
    :return:
        element_by： 元素查找方式，8种基础方式
        element_value： 元素查找表达式
        assist： 辅助属性 eg： text:登录
    '''
    key_list = [By.ID, By.NAME, By.CLASS_NAME, By.TAG_NAME, By.LINK_TEXT,
                By.PARTIAL_LINK_TEXT, By.CSS_SELECTOR, By.XPATH]
    element_by = ''
    element_value = ''
    assist = ''
    for key, value in idname.items():
        if key in key_list:
            element_by = key
            element_value = value
        else:
            assist += '{}:{},'.format(key, value)
    assist = assist.strip(',')
    return element_by, element_value, assist


def display_wait(idname=None, flag=1, timeout=30, driver=None):
    '''
    显示等待，等待元素加载或判断其他条件成立
    :param driver:
    :param idname: 期望出现的元素
    :param flag:
        0：判断指定元素是否加载完成
        1: 判断某个元素是否可见. 可见代表元素非隐藏，并且元素的宽和高都不等于0，传入参数是元组类型的locator
        2：判断元素是否被选中
        3：判断frame是否可切入，可传入locator元组或者直接传入定位方式：id、name、index或WebElement
        4: 判断元素的 text 是否包含预期字符串
        5: 与0相反，判断某个元素消失继续
        6：判断弹框是否出现
        7: 判断元素是否可点击
        other：判断一个元素是否仍在DOM中，传入WebElement对象，可以判断页面是否刷新了
    :param timeout: 超时时间，默认30s
    :return: 得到期望值返回True，如果超时都未找到则返回False
    '''
    ex_type, ex_str, assist = get_params(idname)
    try:
        wait = WebDriverWait(driver=driver, timeout=timeout, poll_frequency=1)
        if flag == 0:
            # logger.debug(f'等待指定元素加载完成:{ex_str}')
            res = wait.until(EC.presence_of_element_located((ex_type, ex_str)))
            return True if res else False
        elif flag == 1:
            # logger.debug(f'等待元素加载可见：{ex_str}')
            res = wait.until(EC.visibility_of_element_located((ex_type, ex_str)))
            return bool(res)
        elif flag == 2:
            # logger.debug(f'判断元素是否选中:{ex_str}')
            wait.until(EC.element_located_to_be_selected((ex_type, ex_str)))
        elif flag == 3:
            # logger.debug(f'等待切换Frame完成:{ex_str}')
            wait.until(EC.frame_to_be_available_and_switch_to_it((ex_type, ex_str)))
        elif flag == 4:
            # logger.debug(f'判断元素是否存在期望的文本:{ex_str}')
            assist_dict = {k: v for item in assist.split(',') for k, v in [item.split(':')]}
            res = wait.until(EC.text_to_be_present_in_element((ex_type, ex_str), assist_dict.get('text')))
            return bool(res)
        elif flag == 5:
            # logger.debug(f'等待元素消失:{ex_str}')
            res = wait.until_not(EC.visibility_of_element_located((ex_type, ex_str)))
            # logger.debug(f'元素消失！')
            return res
        elif flag == 6:
            # logger.debug(f'等待弹框出现!')
            res = wait.until(EC.alert_is_present())
            if res:
                res_text = res.text
                logger.info(f'弹框的文本是： {res_text}')
            return True if res else False
        elif flag == 7:
            res = wait.until(EC.element_to_be_clickable((ex_type, ex_str)))
            return bool(res)
        else:
            # wait.until(EC.staleness_of((ex_type, ex_str)))
            # 假设 idname 是 WebElement 对象
            wait.until(EC.staleness_of(idname))
            return True
    except TimeoutException as e:
        logger.warning(f"等待元素{ex_str}加载超时: {e}")
        return None


def scroll_element_into_container_view(driver, element_xpath, container_xpath=None):
    """
    在容器内滚动元素到可视区域

    :param driver: WebDriver实例
    :param element_xpath: 要滚动到的元素的XPath
    :param container_xpath: 容器元素的XPath(可选，默认为第一个可滚动父元素)
    """
    # 定位目标元素
    element = driver.find_element(By.XPATH, element_xpath)

    # 如果没有指定容器，则查找第一个可滚动的父元素
    if container_xpath is None:
        container = driver.execute_script("""
            var el = arguments[0];
            while (el.parentNode) {
                el = el.parentNode;
                if (el.scrollHeight > el.clientHeight) {
                    return el;
                }
            }
            return document.documentElement; // 默认返回html元素
        """, element)
    else:
        container = driver.find_element(By.XPATH, container_xpath)

    # 执行滚动
    driver.execute_script("""
        var container = arguments[0];
        var element = arguments[1];
        
        // 计算元素相对于容器的位置
        var elementRect = element.getBoundingClientRect();
        var containerRect = container.getBoundingClientRect();
        
        // 计算需要滚动的距离
        var scrollTop = (elementRect.top - containerRect.top) + container.scrollTop - 20; // 20px边距
        
        // 执行滚动
        container.scrollTo({
            top: scrollTop,
            behavior: 'smooth'
        });
    """, container, element)


def scroll_to_element_in_contains(driver, container_xpath, target_text_xpath, step=10):
    """
    滚动到指定元素
    :param driver: WebDriver实例
    :param container_xpath: 滚动容器的xpath定位表达式
    :param target_text_xpath: 目标元素的文本内容
    :param step: 每次滚动的步长，默认为10
    """
    # 先获取当前页面滚动位置
    select_result = True
    error_sys = None
    try:
        div_list = driver.find_element_by_attribute(By.XPATH, container_xpath)
        # 将页面滚动到 顶部
        action = ActionChains(driver=driver)
        action.move_to_element(div_list).perform()
        fundFlag = False
        index = 0
        while index < 10:
            selectLists = div_list.find_elements_by_attribute(By.XPATH, target_text_xpath, timeout=1)
            if len(selectLists) > 0:
                for ele in selectLists:
                    if ele.is_displayed():
                        fundFlag = True
                        break
                if fundFlag:
                    break
            else:
                action.send_keys(Keys.ARROW_DOWN * step).perform()
                index += 1
                logger.warning(f"No.{index}：当前页没有找到目标元素，向下滚动")
        if not fundFlag:
            logger.error(f"下翻查找 {index} 次，仍然没有找到待勾选的元素")
            raise SeleniumError(f"下翻查找 {index} 次，仍然没有找到目标元素")
    except Exception as e:
        logger.error(f"下翻查找元素失败，错误信息：{e}")
        logger.error(traceback.format_exc())
        select_result = False
        error_sys = e
    # 恢复页面滚动位置
    if not select_result:
        raise SeleniumError(f"下翻查找元素失败，错误信息：{error_sys}")
    return select_result
